//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>

#include "ctrl.h"
#include "consoledev.h"

#if !defined(_TEST_TYPE) /* Test console uses serial3 */
//#if !defined(_RELEASE) || defined(_PRERELEASE)
//#define _USBUSBCONSOLE
//#endif
#endif //_TEST_TYPE

#ifdef _DEBUG
#define Assert(x)   if (!((bool)(x))) { \
                        DebugBreak(); \
                    }
#else
#define Assert(x)
#endif

#define MAXLINELENGTH  80

EXTERN_C void PrintComPort(const char *szMessage);
EXTERN_C bool_t CDECL BspReadComPort(ComPort comPort, uint8_t *pu8Data);
EXTERN_C void InitBSPUser();
EXTERN_C UInt32 bspUDCRead(char *pTxBuffer, UInt32 BufLen);
EXTERN_C UInt32 bspUDCWrite(char *pRxBuffer, UInt32 Bufflen);

IDeviceDriver     *g_pConsoleDev = NULL;

EXTERN_C IDeviceDriver *g_pConsoleChannelObj;

EXTERN_C Boolean g_bUsingUSBConsole;

EXTERN_C Boolean g_bUsingConsoleChannel;

//
// The ConsoleDev driver's classfactory
//
EXTERN IDeviceDriver * CDECL CreateConsoleDev()
{
    ECode ec;

    g_pConsoleDev = new ConsoleDev;
    if (NULL == g_pConsoleDev) {
        Assert(0 && "There have no enough memory");
        return NULL;
    }

    // Initialize keyboard hardware
    ec = ((ConsoleDev*)g_pConsoleDev)->Initialize();
    if (FAILED(ec)) {
        delete g_pConsoleDev;
        g_pConsoleDev = NULL;
        return NULL;
    }

    g_pConsoleDev->AddRef();

#if defined(_USBUSBCONSOLE)
    g_bUsingUSBConsole = TRUE;
#endif

    return g_pConsoleDev;
}

ECode ConsoleDev::Initialize()
{
#if defined(_USBUSBCONSOLE)
    InitBSPUser();
#endif

    m_pWriteBuf = (char *)malloc((MAXLINELENGTH + 1) * sizeof(char));
    if (NULL == m_pWriteBuf) {
        return E_OUT_OF_MEMORY;
    }

    m_bConsoleEnabled = TRUE;

    return NOERROR;
}

ECode ConsoleDev::Read(
                /* [in] */ Int64 u64Offset,
                /* [in] */ Int32 bytesToRead,
                /* [out] */ MemoryBuf * pBuffer,
                /* [out] */ IEvent * * ppCompletionEvent)
{
    if (pBuffer == NULL || !pBuffer->GetCapacity()
        || (int)bytesToRead > pBuffer->GetCapacity()
        || 0 == bytesToRead) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_bConsoleEnabled) return NOERROR;

    if (g_bUsingConsoleChannel) {
        if (g_pConsoleChannelObj != NULL) {
            g_pConsoleChannelObj->Read(0, bytesToRead, pBuffer, NULL);
        } // else do nothing
    } else {
#if defined(_USBUSBCONSOLE)
    UInt32 ret;
    char *p = (char*)pBuffer->GetPayload();
    ret = bspUDCRead(p, bytesToRead);
    pBuffer->SetUsed(ret);
#else
    uint8_t *p = (uint8_t *)pBuffer->GetPayload();
    BspReadComPort(COMPORT_PRINT, p);
    pBuffer->SetUsed(1);
#endif
    }

    return NOERROR;
}

ECode ConsoleDev::WriteToCOM(const MemoryBuf & buffer)
{
    uint32_t len;
    char *p = (char *)buffer.GetPayload();
    uint32_t c = buffer.GetUsed();

    while (c > 0) {
        len = (c > MAXLINELENGTH) ? MAXLINELENGTH : c;
        for (uint32_t i = 0; i < len; i++) {
            m_pWriteBuf[i] = *p;
            p++;
        }
        m_pWriteBuf[len] = '\0';
        if (g_bUsingConsoleChannel) {
            if (g_pConsoleChannelObj != NULL) {
                //MemoryBuf ebbBufData((Byte*) m_pWriteBuf, len);
                //g_pConsoleChannelObj->Write(0, ebbBufData, (Int32 *)&len, NULL);
            } // else do nothing
        } else {
#if defined(_USBUSBCONSOLE)
        WaitResult wr;
        m_lock.TryLock(0, &wr);
        if (wr == WaitResult_OK) {
            bspUDCWrite(m_pWriteBuf, len);
            m_lock.Unlock();
        }
#else
        PrintComPort(m_pWriteBuf);
#endif
        }
        c -= len;
    }

    return NOERROR;
}

ECode ConsoleDev::Write(
                /* [in] */ Int64 u64Offset,
                /* [in] */ const MemoryBuf &buffer,
                /* [out] */ Int32 * pBytesWritten,
                /* [out] */ IEvent * * ppCompletionEvent)
{
#if defined(_USBUSBCONSOLE)
    PrintComPort((char *)buffer.GetPayload());
#endif
    if (!m_bConsoleEnabled) {
        return NOERROR;
    }

    return WriteToCOM(buffer);
}

ECode ConsoleDev::Control(
                /* [in] */ Handle32 nControlCode,
                /* [in] */ const MemoryBuf &inBuffer,
                /* [out] */ MemoryBuf * pOutBuffer,
                /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec;

    if ((nControlCode < 0) || (nControlCode > 12)
        || (ppCompletionEvent && *ppCompletionEvent)) {
        return E_INVALID_ARGUMENT;
    }

    switch (nControlCode) {
        case CTRL_SETCURSORPOS: { // Set the Cursor's position
            ec = NOERROR;

            break;
        }

        case CTRL_GETCURSORPOS:  { // Get the Cursor's position
            if (pOutBuffer == NULL ||
                (uint32_t)pOutBuffer->GetCapacity() < sizeof(uint32_t)) {
                return E_INVALID_ARGUMENT;
            }

            *(uint32_t *)pOutBuffer->GetPayload() = 0;//m_uCursorPosition;

            pOutBuffer->SetUsed(sizeof(uint32_t));
            ec = NOERROR;

            break;
        }

        case CTRL_CLEARSCREEN: {  // Clear the screen
            ec = NOERROR;

            break;
        }

        case CTRL_SETCURSORSHAPE: {  // Set the cursor shape
            ec = NOERROR;

            break;
        }

        case CTRL_GETSCREENCOL: {  // Get the screen columns;
            if (pOutBuffer == NULL ||
                (uint32_t)pOutBuffer->GetCapacity() < sizeof(uint32_t)) {
                return E_INVALID_ARGUMENT;
            }

            *(uint32_t *)pOutBuffer->GetPayload() = 320; // m_uScreenColumn;
            pOutBuffer->SetUsed(sizeof(uint32_t));
            ec = NOERROR;

            break;
        }

        case CTRL_GETSCREENROW: {  // Get the screen rows;
            if (pOutBuffer == NULL ||
                (uint32_t)pOutBuffer->GetCapacity() < sizeof(uint32_t)) {
                return E_INVALID_ARGUMENT;
            }

            *(uint32_t *)pOutBuffer->GetPayload() = 240; // m_uScreenRow;
            pOutBuffer->SetUsed(sizeof(uint32_t));
            ec = NOERROR;

            break;
        }

        case CTRL_CONFIGKEYBOARD: {
            ec = S_FALSE;

            break;
        }

        case CTRL_MOVECURSOR:  { //move the cursor forward
            if (inBuffer.GetUsed() < (Int32)sizeof(uint32_t)) {
                return E_INVALID_ARGUMENT;
            }

            uint32_t uMove = *(uint32_t*)inBuffer.GetPayload();
            if (uMove > MAXLINELENGTH) {
                uMove = MAXLINELENGTH;
            }

            for (uint32_t i = 0; i < uMove; i++) {
                m_pWriteBuf[i] = '\b';
            }
            m_pWriteBuf[uMove] = '\0';
            if (g_bUsingConsoleChannel) {
                if (g_pConsoleChannelObj != NULL) {
                    UInt32 sent = 0;
                    do {
                        MemoryBuf ebbData((Byte*) m_pWriteBuf + sent, uMove - sent);
                        Int32 len = 0;
                        UInt32 ret = g_pConsoleChannelObj->Write(0, ebbData, &len, NULL);
                        if (ret != 0) {
                            ec = S_FALSE; // failed!
                            break;
                        }
                        sent += len;
                    } while (sent < uMove);

#if defined(_USBUSBCONSOLE)
                    PrintComPort(m_pWriteBuf);
#endif
                } // else do nothing
            } else {
#if defined(_USBUSBCONSOLE)
            bspUDCWrite(m_pWriteBuf, uMove);
#else
            PrintComPort(m_pWriteBuf);
#endif
            }
            ec = NOERROR;

            break;
        }

        case CTRL_DISABLECONSOLE: { //disable console
            m_bConsoleEnabled = FALSE;
            //enablde serial3
#if defined(_USBUSBCONSOLE)

#else
            DzEnableIrq(37); //UART0
#endif
            ec = NOERROR;
            break;
        }

        case CTRL_ENABLECONSOLE: { //enable console
            m_bConsoleEnabled = TRUE;
            //disablde serial3
#if defined(_USBUSBCONSOLE)

#else
            DzDisableIrq(37); //UART0
#endif
            ec = NOERROR;
            break;
        }

        default:
            ec = E_NOT_IMPLEMENTED;
    }

    return ec;
}
